use core::fmt::Write;
use core::ptr::{self, addr_of, addr_of_mut, null};
use core::slice;

use mlibs::cmsis_rtos2::event_flags::EventFlags;
use mlibs::cmsis_rtos2::message_queue::{mq_size, MessageQueue};
use mlibs::cmsis_rtos2::OS_WAIT_FOREVER;
use log::{LevelFilter, Log};
use mlibs::periph::dma::DmaBuf;
use mlibs::periph::uart::UART1;
use mlibs::periph::{interrupt, Interrupt, dma::DMA1, NVIC};
use mlibs::periph::{config_dma, config_io};
use mlibs::periph::rcc::RCC;
use mlibs::periph::gpio::PA;

#[config_io(
    TX_PA9_AF_PP,
    RX_PA10_IN_FLOAT
)]
struct UartPin;

#[config_dma(DMA1_CH4_SIZE8)]
struct TxDma;

#[config_dma(DMA1_CH5_SIZE8)]
struct RxDma;
const MQ_SIZE: usize = mq_size::<ByteView>(5);
static mut EVENT_FLAGS: EventFlags = EventFlags::default();
static mut MQ: MessageQueue<ByteView, 5, MQ_SIZE> = MessageQueue::default();

static mut TX_BUF: DmaBuf<u8, 256> = DmaBuf::default();
static mut RX_BUF: DmaBuf<u8, 64> = DmaBuf::default();

const FLAG_DMA_TC: u32 = 0x01;
const FLAG_LOG: u32 = 0x02;


pub fn uart_init() {
    
    UartPin.init();
    RCC.apb2enr.modify(|_, w| {
        w.usart1en().enabled()
    });
    TxDma.init_rcc();
    RxDma.init_rcc();
    
    UART1.cr1.reset();
    UART1.cr2.reset();
    UART1.cr3.write(|w| {
        w.dmar().enabled()
        .dmat().enabled()
    });
    UART1.brr.write(|w| unsafe {
        w.bits(72000000 / 115200)
    });
    UART1.cr1.write(|w| {
        w.ue().enabled()
        .m().m8()
        .idleie().enabled()
        .te().enabled()
        .re().enabled()
    });
    unsafe {
        EVENT_FLAGS.new("uart1");
        _ = EVENT_FLAGS.set_flags(FLAG_DMA_TC | FLAG_LOG);
        MQ.new("uart1");

        RxDma.read_with_circ(UART1.dr.as_ptr() as u32, RX_BUF.as_mut_ref());

        NVIC::unmask(Interrupt::USART1);
        NVIC::unmask(Interrupt::DMA1_CHANNEL4);
    }
    _ = log::set_logger(&Uart);
    _ = log::set_max_level(LevelFilter::Debug);
}

pub unsafe fn uart_write(buf: &[u8]) {
    _ = EVENT_FLAGS.wait_any_flags(FLAG_DMA_TC, OS_WAIT_FOREVER);
    let len = buf.len();
    ptr::copy_nonoverlapping(buf.as_ptr(), TX_BUF.as_mut_ref().as_mut_ptr(), len);
    TxDma.write_with_it(UART1.dr.as_ptr() as u32, TX_BUF.as_slice(len));
}

pub fn uart_receive() -> &'static[u8] {
    unsafe {
        let bw = MQ.poll(OS_WAIT_FOREVER).unwrap();
        let s = slice::from_raw_parts(bw.ptr, bw.len as usize);
        s
    }
}

struct Uart;

impl Write for Uart {

    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        unsafe { uart_write(s.as_bytes()); }
        Ok(())
    }
} 

impl Log for Uart {

    fn enabled(&self, _metadata: &log::Metadata) -> bool {
        true
    }

    fn flush(&self) {
        
    }

    fn log(&self, record: &log::Record) {
        let mut u = Uart;
        unsafe {
            _ = EVENT_FLAGS.wait_any_flags(FLAG_LOG, OS_WAIT_FOREVER);
        }
        _ = writeln!(u, "[{}] {}:{} => {}",
            record.level(), 
            record.file_static().unwrap_or(""),
            record.line().unwrap_or(0),
            record.args()
        );
        unsafe {
            _ = EVENT_FLAGS.set_flags(FLAG_LOG);
        }
    }
}

static mut RECV_INDEX: u16 = 0;

struct ByteView {
    ptr: *const u8,
    len: u16,
}

impl Default for ByteView {

    fn default() -> Self {
        Self { ptr: null(), len: 0 }
    }
}

unsafe fn recv_idle() {
    let index = RX_BUF.size() as u16 - RxDma.ndtr();
    let recv_index = ptr::read_volatile(addr_of!(RECV_INDEX));

    if index == recv_index {
        return;
    }
    if index > recv_index {
        let bw = ByteView {
            ptr: RX_BUF.as_ptr().add(recv_index as usize),
            len: index - recv_index,
        };
        _ = MQ.post(&bw);
    } else {
        let bw = ByteView {
            ptr: RX_BUF.as_ptr().add(recv_index as usize),
            len: RX_BUF.size() as u16 - recv_index
        };
        _ = MQ.post(&bw);
        if index != 0 {
            let bw = ByteView {
                ptr: RX_BUF.as_ptr(),
                len: index
            };
            _ = MQ.post(&bw);
        }
    }
    ptr::write_volatile(addr_of_mut!(RECV_INDEX), index);
}

#[interrupt]
unsafe fn USART1() {
    if UART1.sr.read().idle().bit_is_set() {
        UART1.dr.read().dr().bits();
        recv_idle();
    }
}

#[interrupt]
unsafe fn DMA1_CHANNEL4() {
    if TxDma.is_complete() {
        TxDma.clear_flags();
        _ = EVENT_FLAGS.set_flags(FLAG_DMA_TC);
    }    
}


